﻿//////////////////////////////////////////////////////////////////////////////////////////////////
// This class is to deal with static variables in memory and offline/cloud storage on local machine
// Crated by Gary Zhao
/////////////////////////////////////////////////////////////////////////////////////////////////
using System;
using System.Net;
using System.Net.NetworkInformation;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO;
using System.IO.IsolatedStorage;
using System.ServiceModel;
using System.Runtime.Serialization.Json;
using System.Runtime.Serialization;
using System.Text;
using System.ServiceModel.Channels;
using System.Json;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.ComponentModel;

using TwitterBEIS_SL3_SLApp.TwitterProxyReference;

namespace TwitterBEIS_SL3_SLApp
{
    enum TwitterDataType {Status, User, EUser, DM };
    public static class Storage
    {
        const string SecretFile = "secret";
        const string FriendTimelineFile = "friendstimeline";
        const string RepliesFile = "replies";
        const string DMsFile = "dms";
        const string SentFile = "sent";
        const string FriendsFile = "friends";
        const string TweetsFile = "tweets";

        static MainUI mainui;

        private static string id;
        private static string username;
        public static string UserName { get { return username; } }
        private static string password;
        public static string Password { get { return password; } }
        private static bool remember;
        public static bool Remember { get { return remember; } }

        private static TwitterProxyReference.TwitterProxyClient proxy = TwitterProxy.GetProxy();

        private static List<string> OfflineTwitters;
        private static bool updating = false;
        private static bool syncing = false;

        static Storage()
        {
            NetworkChange.NetworkAddressChanged += new NetworkAddressChangedEventHandler(NetworkChange_NetworkAddressChanged);

            proxy.FriendsTimeLineCompleted += new EventHandler<FriendsTimeLineCompletedEventArgs>(proxy_FriendsTimeLineCompleted);
            proxy.RepliesCompleted += new EventHandler<RepliesCompletedEventArgs>(proxy_RepliesCompleted);
            proxy.DirectMessagesCompleted += new EventHandler<DirectMessagesCompletedEventArgs>(proxy_DirectMessagesCompleted);
            proxy.UserTimelineCompleted += new EventHandler<UserTimelineCompletedEventArgs>(proxy_UserTimelineCompleted);
            proxy.FriendsCompleted += new EventHandler<FriendsCompletedEventArgs>(proxy_FriendsCompleted);
            proxy.UpdateCompleted += new EventHandler<UpdateCompletedEventArgs>(proxy_UpdateCompleted);
            proxy.NewDMCompleted += new EventHandler<NewDMCompletedEventArgs>(proxy_NewDMCompleted);

        }

        static void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)
        {
            if (!NetworkInterface.GetIsNetworkAvailable())
                return;
            else
            {
                if (!updating)
                {
                    //If no tweets saved locally, we sync. Otherwize, we don't sync until all tweets posted to Twitter successfully.
                    if (TweetsCount() == 0)
                    {
                        SyncData();
                    }
                    else
                    {
                        Storage.PostTweets();
                    }
                }
            }
        }
        //////////////////////////////////////////////////////////////////////////
        //IsolatedStorage operations
        /////////////////////////////////////////////////////////////////////////
        static public bool ReadSecret()
        {
            try
            {
                using (var store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    if (store.FileExists(SecretFile))
                    {

                        IsolatedStorageFileStream stream;
                        stream = store.OpenFile(SecretFile, FileMode.Open, FileAccess.Read);
                        StreamReader reader = new StreamReader(stream);

                        id = reader.ReadLine();
                        username = reader.ReadLine();
                        password = reader.ReadLine();
                        remember = Convert.ToBoolean(reader.ReadLine());
                        reader.Close();
                        stream.Close();
                    }
                    else
                        return false;
                }
                return true;
            }
            catch
            {
                id = null;
                username = null;
                password = null;
                remember = false;
                return false;
            }
        }

        static public void SaveSecret(byte[] input, string name, string password, bool remember)
        {
            MemoryStream memstream = new MemoryStream(input);
            JsonObject obj = (JsonObject)JsonObject.Load(memstream);

            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                IsolatedStorageFileStream stream;
                stream = store.OpenFile("secret",FileMode.Create,FileAccess.Write);
                StreamWriter writer = new StreamWriter(stream);
                writer.WriteLine(obj["id"].ToString());
                writer.WriteLine(name);
                writer.WriteLine(password);
                writer.WriteLine(remember.ToString());
                writer.Flush();
                writer.Close();
                stream.Close();
            }

            memstream.Close();
        }

        /// Deal with tweets
        static private void SaveTweet(string tweet)
        {
            if (TweetsCount() >= 10)
            {
                mainui.Warning("Can't post more than 10 tweets when offline.");
                return;
            }

            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                IsolatedStorageFileStream stream = store.OpenFile(TweetsFile, FileMode.Append, FileAccess.Write);
                StreamWriter writer = new StreamWriter(stream);

                writer.WriteLine(tweet);
                writer.Flush();
                writer.Close();
                stream.Close();
            }
        }

        static private void DeleteTweets()
        {
            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (store.FileExists(TweetsFile))
                {
                    store.DeleteFile(TweetsFile);
                }
            }
        }

        /// <summary>
        /// Two situations to trigger PostTweets
        /// 1. Authentication passed
        /// 2. Offline -> Online
        /// </summary>
        static public void PostTweets()
        {
            if (NetworkInterface.GetIsNetworkAvailable())
            {
                OfflineTwitters = ReadTweets();
                if (OfflineTwitters != null)
                {
                    updating = true;
                    PostTweet(OfflineTwitters[0]);
                }
            }
        }

        static private List<string> ReadTweets()
        {
            int count = TweetsCount();
            if (count == 0)
                return null;

            List<string> tweets = new List<string>();
            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                IsolatedStorageFileStream stream;
                stream = store.OpenFile(TweetsFile, FileMode.Open, FileAccess.Read);
                StreamReader reader = new StreamReader(stream);

                for (int i = 0; i < count; i++)
                    tweets.Add(reader.ReadLine());
                reader.Close();
                stream.Close();
            }

            return tweets;
        }

        static public int TweetsCount()
        {
            int count = 0;
            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (store.FileExists(TweetsFile))
                {
                    IsolatedStorageFileStream stream;
                    stream = store.OpenFile(TweetsFile, FileMode.Open, FileAccess.Read);
                    StreamReader reader = new StreamReader(stream);

                    while (reader.ReadLine()!=null)
                    {
                        count++;
                    }

                    reader.Close();
                    stream.Close();
                }
             }
            return count;
        }

        static private void PostTweet(string tweet)
        {
            tweet=tweet.Trim();
            if (tweet[0] == 'd' && tweet[1] == ' ')
            {
                char[] delimiter={' '};
                string[] parts=tweet.Split(delimiter);
                if(parts.Length<3)
                {
                    mainui.Warning("must follow \"d user message\"");
                }
                else
                {
                    string user=parts[1];
                    string text="";
                    for (int i = 2; i < parts.Length; i++)
                    {
                        text += parts[i];
                        text += " ";
                    }
                    text=text.Trim();
                    proxy.NewDMAsync(username,password,user,text);
                }
            }
            else
            {
                proxy.UpdateAsync(username, password, tweet);
            }
        }
        /// Deal with tweets
        /// if online, it's posted to twitter immediately, otherwize it'll be saved locally
        static public void Update(MainUI ui, string tweet)
        {
            mainui = ui;

            if (NetworkInterface.GetIsNetworkAvailable())
            {
                PostTweet(tweet);
            }
            else
            {
                SaveTweet(tweet);
            }

            mainui.Init();
        }

        static private void PostCompleted(AsyncCompletedEventArgs e)
        {
            if (OfflineTwitters==null)
            {
                if (e.Error == null)
                {
                    SyncData();
                }
                else
                {
                    mainui.Warning(e.Error.Message);
                }

                return;
            }

            if (e.Error==null)
            {
                OfflineTwitters.RemoveAt(0);
            }
            if (OfflineTwitters.Count != 0)
            {
                PostTweet(OfflineTwitters[0]);
            }
            else
            {
                OfflineTwitters = null;
                updating = false;
                DeleteTweets();
                SyncData();  
            }
        }

        static void proxy_UpdateCompleted(object sender, UpdateCompletedEventArgs e)
        {
            PostCompleted(e);
        }

        static void proxy_NewDMCompleted(object sender, NewDMCompletedEventArgs e)
        {
            PostCompleted(e);
        }
        /// Sync be triggered by four conditions
        /// 1. App starts in online mode (after logon)
        /// 2. Timer (5 minutes) times out
        /// 3. Tweet posted
        /// 4. Chaning from offline to online
        /// After sync, the current page should be updated, otherwize the page will take data from local storage
        /// </summary>
        static public void SyncData()
        {
            if (syncing==false&&NetworkInterface.GetIsNetworkAvailable())
            {
                syncing = true;
                proxy.FriendsTimeLineAsync(username, password);
            }
        }

        static private void SaveStream(string file, byte[] input)
        {
            if (input == null)
                return;
            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                IsolatedStorageFileStream stream = store.OpenFile(file, FileMode.Create, FileAccess.Write);
                BinaryWriter writer = new BinaryWriter(stream);

                writer.Write(input);
                writer.Flush();
                writer.Close();
                stream.Close();
            }
        }

        static private void proxy_FriendsTimeLineCompleted(object sender, FriendsTimeLineCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                if (mainui.Pagetype == PageType.UPdates)
                {
                    SetMainUIFromCloud(e.Result, TwitterDataType.Status);
                }
                SaveStream(FriendTimelineFile, e.Result);

                proxy.RepliesAsync(username, password);
            }
            else
            {
                proxy.FriendsTimeLineAsync(username, password);
            }

        }

        static void proxy_RepliesCompleted(object sender, RepliesCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                if (mainui.Pagetype == PageType.Replies)
                {
                    SetMainUIFromCloud(e.Result, TwitterDataType.Status);
                }
                SaveStream(RepliesFile, e.Result);

                proxy.DirectMessagesAsync(username, password);
            }
            else
            {
                proxy.RepliesAsync(username, password);
            }
        }

        static void proxy_DirectMessagesCompleted(object sender, DirectMessagesCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                if (mainui.Pagetype == PageType.DMs)
                {
                    SetMainUIFromCloud(e.Result, TwitterDataType.DM);
                }
                SaveStream(DMsFile, e.Result);

                proxy.UserTimelineAsync(username, password, id);
            }
            else
            {
                proxy.DirectMessagesAsync(username, password);
            }
        }

        static void proxy_UserTimelineCompleted(object sender, UserTimelineCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                if (mainui.Pagetype == PageType.Sent)
                {
                    SetMainUIFromCloud(e.Result, TwitterDataType.Status);
                }
                SaveStream(SentFile, e.Result);

                proxy.FriendsAsync(username, password, id);
            }
            else
            {
                proxy.UserTimelineAsync(username, password,id);
            }
        }

        static void proxy_FriendsCompleted(object sender, FriendsCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                if (mainui.Pagetype == PageType.Friends)
                {
                    SetMainUIFromCloud(e.Result, TwitterDataType.User);
                }
                SaveStream(FriendsFile, e.Result);

                syncing = false;
            }
            else
            {
                proxy.FriendsAsync(username, password,id);
            }
            
        }
        //////////////////////////////////////////////////////////////////////////////////////////////////
        //Called from MainUI
        //Start
        static public void SetFriendsTimeLine(MainUI ui)
        {
            mainui = ui;
            SetMainUIFromLocal(FriendTimelineFile, TwitterDataType.Status);
        }

        static public void SetReplies(MainUI ui)
        {
            mainui = ui;
            SetMainUIFromLocal(RepliesFile, TwitterDataType.Status);
        }

        static public void SetDMs(MainUI ui)
        {
            mainui = ui;
            SetMainUIFromLocal(DMsFile, TwitterDataType.DM);
        }

        static public void SetSent(MainUI ui)
        {
            mainui = ui;
            SetMainUIFromLocal(SentFile, TwitterDataType.Status);
        }

        static public void SetFriends(MainUI ui)
        {
            mainui = ui;
            SetMainUIFromLocal(FriendsFile, TwitterDataType.User);
        }
        //End
        //Called from MainUI
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        static private void SetMainUIFromJsonArry(JsonArray array, TwitterDataType type)
        {
            mainui.UpdatesList.Items.Clear();
            if (mainui.Pagetype == PageType.Sent&&TweetsCount()!=0)
            {
                List<string> tweets = ReadTweets();
                foreach (string tweet in tweets)
                {
                    mainui.UpdatesList.Items.Add(new Tweet(null,username,tweet,"Offline"));
                }
            }
            foreach (JsonObject obj in array)
            {
                Tweet tweet = new Tweet();
                switch (type)
                {
                    case TwitterDataType.Status:
                        tweet.SetTweet(obj["user"]["profile_image_url"], obj["user"]["screen_name"], obj["text"], obj["created_at"]);
                        break;
                    case TwitterDataType.DM:
                        tweet.SetTweet(obj["sender"]["profile_image_url"], obj["sender"]["screen_name"], obj["text"], obj["created_at"]);
                        break;
                    case TwitterDataType.User:
                        tweet.SetTweet(obj["profile_image_url"], obj["screen_name"], obj["status"]["text"], obj["location"]);
                        break;
                }
                mainui.UpdatesList.Items.Add(tweet);
            }
        }

        static private void SetMainUIFromCloud(byte[] input, TwitterDataType type)
        {
            MemoryStream stream = new MemoryStream(input);
            JsonArray array = (JsonArray)JsonArray.Load(stream);
            SetMainUIFromJsonArry(array, type);
            stream.Close();
        }

        static private void SetMainUIFromLocal(string file, TwitterDataType type)
        {
            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (store.FileExists(file))
                {
                    IsolatedStorageFileStream stream;
                    stream = store.OpenFile(file, FileMode.Open, FileAccess.Read);
                    JsonArray array = (JsonArray)JsonArray.Load(stream);
                    SetMainUIFromJsonArry(array, type);
                    stream.Close();
                }
            }
        }
    }
}
